﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.EdgeGraph;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Dissolve
{
	public class LineDissolver
	{
		public static IGeometry Dissolve(IGeometry g)
		{
			LineDissolver lineDissolver = new LineDissolver();
			lineDissolver.Add(g);
			return lineDissolver.GetResult();
		}

		public LineDissolver()
		{
			this._graph = new DissolveEdgeGraph();
		}

		public void Add(IGeometry geometry)
		{
			geometry.Apply(new GeometryComponentFilter(delegate(IGeometry c)
			{
				if (c is ILineString)
				{
					this.Add(c as ILineString);
				}
			}));
		}

		public void Add(IEnumerable<IGeometry> geometries)
		{
			foreach (IGeometry geometry in geometries)
			{
				this.Add(geometry);
			}
		}

		private void Add(ILineString lineString)
		{
			if (this._factory == null)
			{
				this._factory = lineString.Factory;
			}
			bool flag = false;
			ICoordinateSequence coordinateSequence = lineString.CoordinateSequence;
			for (int i = 1; i < coordinateSequence.Count; i++)
			{
				Coordinate coordinate = coordinateSequence.GetCoordinate(i - 1);
				Coordinate coordinate2 = coordinateSequence.GetCoordinate(i);
				DissolveHalfEdge dissolveHalfEdge = (DissolveHalfEdge)this._graph.AddEdge(coordinate, coordinate2);
				if (dissolveHalfEdge != null && !flag)
				{
					dissolveHalfEdge.SetStart();
					flag = true;
				}
			}
		}

		public IGeometry GetResult()
		{
			if (this._result == null)
			{
				this.ComputeResult();
			}
			return this._result;
		}

		private void ComputeResult()
		{
			foreach (HalfEdge e in this._graph.GetVertexEdges())
			{
				if (!MarkHalfEdge.IsMarked(e))
				{
					this.Process(e);
				}
			}
			this._result = this._factory.BuildGeometry(this._lines);
		}

		private void Process(HalfEdge e)
		{
			HalfEdge halfEdge = e.PrevNode();
			if (halfEdge == null)
			{
				halfEdge = e;
			}
			this.StackEdges(halfEdge);
			this.BuildLines();
		}

		private void BuildLines()
		{
			while (this._nodeEdgeStack.Count > 0)
			{
				HalfEdge halfEdge = this._nodeEdgeStack.Pop();
				if (!MarkHalfEdge.IsMarked(halfEdge))
				{
					this.BuildLine(halfEdge);
				}
			}
		}

		private void UpdateRingStartEdge(DissolveHalfEdge e)
		{
			if (!e.IsStart)
			{
				e = (DissolveHalfEdge)e.Sym;
				if (!e.IsStart)
				{
					return;
				}
			}
			if (this._ringStartEdge == null)
			{
				this._ringStartEdge = e;
				return;
			}
			Coordinate orig = e.Orig;
			Coordinate orig2 = this._ringStartEdge.Orig;
			if (orig.CompareTo(orig2) < 0)
			{
				this._ringStartEdge = e;
			}
		}

		private void BuildLine(HalfEdge eStart)
		{
			CoordinateList coordinateList = new CoordinateList();
			DissolveHalfEdge dissolveHalfEdge = (DissolveHalfEdge)eStart;
			this._ringStartEdge = null;
			MarkHalfEdge.MarkBoth(dissolveHalfEdge);
			Coordinate orig = dissolveHalfEdge.Orig;
			coordinateList.Add(orig.Clone(), false);
			while (dissolveHalfEdge.Sym.Degree() == 2)
			{
				this.UpdateRingStartEdge(dissolveHalfEdge);
				DissolveHalfEdge dissolveHalfEdge2 = (DissolveHalfEdge)dissolveHalfEdge.Next;
				if (dissolveHalfEdge2 == eStart)
				{
					this.BuildRing(this._ringStartEdge);
					return;
				}
				orig = dissolveHalfEdge2.Orig;
				coordinateList.Add(orig.Clone(), false);
				dissolveHalfEdge = dissolveHalfEdge2;
				MarkHalfEdge.MarkBoth(dissolveHalfEdge);
			}
			Coordinate dest = dissolveHalfEdge.Dest;
			coordinateList.Add(dest.Clone(), false);
			this.StackEdges(dissolveHalfEdge.Sym);
			this.AddLine(coordinateList);
		}

		private void BuildRing(HalfEdge eStartRing)
		{
			CoordinateList coordinateList = new CoordinateList();
			HalfEdge halfEdge = eStartRing;
			Coordinate orig = halfEdge.Orig;
			coordinateList.Add(orig.Clone(), false);
			while (halfEdge.Sym.Degree() == 2)
			{
				HalfEdge next = halfEdge.Next;
				if (next == eStartRing)
				{
					break;
				}
				orig = next.Orig;
				coordinateList.Add(orig.Clone(), false);
				halfEdge = next;
			}
			Coordinate dest = halfEdge.Dest;
			coordinateList.Add(dest.Clone(), false);
			this.AddLine(coordinateList);
		}

		private void AddLine(CoordinateList line)
		{
			Coordinate[] coordinates = line.ToCoordinateArray();
			ILineString item = this._factory.CreateLineString(coordinates);
			this._lines.Add(item);
		}

		private void StackEdges(HalfEdge node)
		{
			HalfEdge halfEdge = node;
			do
			{
				if (!MarkHalfEdge.IsMarked(halfEdge))
				{
					this._nodeEdgeStack.Push(halfEdge);
				}
				halfEdge = halfEdge.ONext;
			}
			while (halfEdge != node);
		}

		private IGeometry _result;

		private IGeometryFactory _factory;

		private readonly DissolveEdgeGraph _graph;

		private readonly IList<IGeometry> _lines = new List<IGeometry>();

		private readonly Stack<HalfEdge> _nodeEdgeStack = new Stack<HalfEdge>();

		private DissolveHalfEdge _ringStartEdge;
	}
}
